package genstream.pump

import java.io.{OutputStream, InputStream}
import genstream._
import scaladuct.{Name, Ref, Starter}

/**
 * Created by IntelliJ IDEA.
 * User: zslajchrt
 * Date: 12.4.11
 * Time: 14:22
 * To change this template use File | Settings | File Templates.
 */

class Pump(inputResource: InputResource, outputResource: OutputResource, bufferSize: Int = 1024 * 1024,
           closeOutput: Boolean = true) extends Runnable {

  def this(cookie: PumpStarterCookie) = this(cookie.input.newInstance, cookie.output.newInstance, cookie.bufferSize)

  protected def createContext(): StreamingContext = new StreamingContext() {}

  protected def copyData(inputStream: InputStream, outputStream: OutputStream) {
    val buffer = new Array[Byte](bufferSize)
    var n = 0
    do {
      n = inputStream.read(buffer, 0, buffer.length)
      if (n > 0)
        outputStream.write(buffer, 0, n)
    } while (n >= 0)
  }

  def run = {
    val ctx = createContext
    val iterator = inputResource.openIterator(ctx)
    val outputStream = outputResource.openFragmentedOutputStream(ctx)

    var inputHolder = iterator.next
    try {
      while (inputHolder != null) {
        val inputStream = inputHolder.getInputStream

        outputStream.onStartFragment(inputHolder)
        copyData(inputStream, outputStream)
        outputStream.onEndFragment()

        inputHolder = iterator.next
      }
    }
    finally {
      try {
        iterator.close
      }
      finally {
        if (closeOutput)
          outputStream.close
        else
          outputStream.flush
      }
    }

  }

}

@serializable
@SerialVersionUID(1L)
class PumpStarterCookie(
  var bufferSize: Int = 1024 * 1024,
  var input: ServiceFactory[InputResource] = null,
  var output: ServiceFactory[OutputResource] = null
)


@SerialVersionUID(1L)
class PumpStarter extends Starter[ServiceFactory[Pump]] {
  def newInstance(): ServiceFactory[Pump] = phaseInitialize(1024 * 1024, null, null)

  def phaseInitialize(@Name("bufferSize") bufferSize: Int,
                      @Name("input") @Ref input: ServiceFactory[InputResource],
                      @Name("output") @Ref output: ServiceFactory[OutputResource]): ServiceFactory[Pump] = {
    val cookie = new PumpStarterCookie(bufferSize, input, output)
    new FunctionalServiceFactory(new Pump(cookie))
  }

  def destroy(instance: ServiceFactory[Pump]) = null

}